package com.test.A05;

import java.util.Set;

import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

 

/*
    BeanFactory 后处理器的作用
 */
public class A05 {
    private static final Logger log = LoggerFactory.getLogger(A05.class);

    public static void main(String[] args) throws Exception {
    	test6();
       
    }
    
	/***
	 * 使用自定义的MapperPostProcessor模拟@Mapper
	 * @throws Exception
	 */
    public static void test6() throws  Exception {
    	  GenericApplicationContext context = new GenericApplicationContext();
          context.registerBean("config", Config.class); 
          context.registerBean(com.test.A05.AtBeanPostProcessor.class); //使用自定义的解析器  解析 @Bean  测试下边的@mapper的话，需要把@Bean加到bean工厂中
          context.registerBean(com.test.A05.MapperPostProcessor.class);//通过自定义的mapper后处理器来解析@mapper
          //  初始化容器
          context.refresh();
          for (String name : context.getBeanDefinitionNames()) {
              System.out.println(name);
          }
          //  销毁容器
          context.close();
    }
    
	/***
	 * 使用自定义的AtBeanPostProcessor模拟@Bean
	 * @throws Exception
	 */
    public static void test5() throws  Exception {
    	  GenericApplicationContext context = new GenericApplicationContext();
          context.registerBean("config", Config.class); 
          context.registerBean(com.test.A05.AtBeanPostProcessor.class); //使用自定义的解析器  解析 @Bean
          //  初始化容器
          context.refresh();
          for (String name : context.getBeanDefinitionNames()) {
              System.out.println(name);
          }
          //  销毁容器
          context.close();
    }
    
    /****
     * 模拟@bean注解
     * @throws Exception
     */
	public static void test4() throws Exception {
		GenericApplicationContext context = new GenericApplicationContext();
		context.registerBean("config", Config.class);
		//得到配置类的路径 
		CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
		MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/test/A05/Config.class"));
		
		//查看方法上是否有@bean的注解
		Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
		for (MethodMetadata method : methods) {
			//System.out.println(method);
			//得到bean注解的属性initMethod
		 	String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
			//创建bean定义
			BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
			//把bean定义设置到那个工厂方法上
			builder.setFactoryMethodOnBean(method.getMethodName(), "config");
		    builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);//构造方法  工厂方法 
			 if (initMethod.length() > 0) {
			 	builder.setInitMethodName(initMethod);
			 }
			//得到bean定义
			AbstractBeanDefinition bd = builder.getBeanDefinition();
			//把bean定义注册到bean工厂上
			 context.getDefaultListableBeanFactory().registerBeanDefinition(method.getMethodName(), bd);
		}
		
		//  初始化容器
		context.refresh();
		for (String name : context.getBeanDefinitionNames()) {
			System.out.println(name);
		}
		//  销毁容器
		 context.close();
	 }
    
	/***
	 * 使用自定义的ComponentScanPostProcessor模拟@ComponentScan
	 * @throws Exception
	 */
    public static void test3() throws  Exception {
    	  GenericApplicationContext context = new GenericApplicationContext();
          context.registerBean("config", Config.class); 
          context.registerBean(com.test.A05.ComponentScanPostProcessor.class); //使用自定义的解析器  解析 @ComponentScan
          // ⬇️初始化容器
          context.refresh();
          for (String name : context.getBeanDefinitionNames()) {
              System.out.println(name);
          }
          // ⬇️销毁容器
          context.close();
    }
    
    
    /****
     * 模拟@ComponentScan  
     * 一、通过配置类得到扫描的包  
     * 二、把包路径转换成文件路径
     * 三、读取所有的文件  得到文件的头路径是否是Component或者它的派生类  判断是否有注解
     * 四、利用工具类BeanDefinitionBuilder通过类全名称得到bean定义 对象
     * 五、利用工具类AnnotationBeanNameGenerator生成bean的名称
     * 六、把bean定义注册到bean工厂  
     * 七、循环得到bean的定义
     * @throws Exception
     */
    public static void test2() throws  Exception {
    	 // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class); 
        //得到配置文件，扫描注解的路径 ComponentScan
        ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
        if (componentScan != null) {
            for (String p : componentScan.basePackages()) {
                //System.out.println(p);
                //  com.test.A05.component -> classpath*:com/test/A05/component/**/*.class
                String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
               // System.out.println(path);
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                for (Resource resource : resources) {
                    //System.out.println(resource);
                    //file [E:\workwang\lianxieclipse\ruyifast\springdemo\target\classes\com\test\A05\component\Bean4.class]
                    MetadataReader reader = factory.getMetadataReader(resource);
                     //System.out.println("类名:" + reader.getClassMetadata().getClassName());
                    AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                   //   System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
                    // System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
                    if (annotationMetadata.hasAnnotation(Component.class.getName())
                            || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {//判断是否有@Component或者@Component的派生注解
                            AbstractBeanDefinition bd = BeanDefinitionBuilder
                                    .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();//通过工具类得到bean定义
                            DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
                            String name = generator.generateBeanName(bd, beanFactory);//使用AnnotationBeanNameGenerator工具来生成bean别名
                            beanFactory.registerBeanDefinition(name, bd);//把bean定义注册到bean工厂
                        }
                }
            }
        }
        // ⬇️初始化容器
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        // ⬇️销毁容器
        context.close();
    }
    
    /*****
     * bean的后处理器 MapperScannerConfigurer和ConfigurationClassPostProcessor
     */
    public static void test1() {
    	 // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
	    
        //扫描@Mapper
        context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
	       bd.getPropertyValues().add("basePackage", "com.test.A05.mapper");
	       //设置扫描的包名
	   });
        // ⬇️初始化容器
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        // ⬇️销毁容器
        context.close();
    }
}
